﻿using System;
using System.Collections.Generic;
using System.Reflection;
using System.Threading;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Web7.Caching;
using Web7.Extensions;

namespace Web7.Tests.Web7.Caching
{
    /// <summary>
    /// AbstractCacheProvider 的摘要说明
    /// </summary>
    [TestClass]
    public abstract class AbstractCacheProviderFixture
    {
        private TestContext _testContextInstance;

        /// <summary>
        ///获取或设置测试上下文，该上下文提供
        ///有关当前测试运行及其功能的信息。
        ///</summary>
        public TestContext TestContext
        {
            get
            {
                return _testContextInstance;
            }
            set
            {
                _testContextInstance = value;
            }
        }

        #region 附加测试特性
        //
        // 编写测试时，可以使用以下附加特性:
        //
        // 在运行类中的第一个测试之前使用 ClassInitialize 运行代码
        // [ClassInitialize()]
        // public static void MyClassInitialize(TestContext testContext) { }
        //
        // 在类中的所有测试都已运行之后使用 ClassCleanup 运行代码
        // [ClassCleanup()]
        // public static void MyClassCleanup() { }
        //
        // 在运行每个测试之前，使用 TestInitialize 来运行代码
        // [TestInitialize()]
        // public void MyTestInitialize() { }
        //
        // 在每个测试运行完之后，使用 TestCleanup 来运行代码
        // [TestCleanup()]
        // public void MyTestCleanup() { }
        //
        #endregion

        [TestInitialize]
        public abstract void SetUp();

        protected AbstractCacheProvider CacheProvider { get; set; }

        [TestCleanup]
        public void ClassCleaup()
        {
            CacheProvider.Dispose();
        }

        /// <summary>
        /// 滑动过期
        /// </summary>
        [TestMethod]
        public void SlidingExpiredItemIsRemoved()
        {
            var stringKey = Web7CacheKey.Create("hello");
            var myObject1 = CacheProvider.GetOrCreate(stringKey,
                () => new CacheValueOf<TestCacheObject>(new TestCacheObject("hello-1"), new StaticCachePolicy(TimeSpan.FromSeconds(2))));

            Assert.IsNotNull(myObject1);

            Assert.AreEqual(myObject1.AlreadyExisted, false); //非以存在的

            Assert.AreEqual(myObject1.WasInserted, true); //表示该缓存是插入状态

            Assert.IsNotNull(myObject1.Value); //缓存值不为空

            Assert.IsNotNull(CacheProvider.GetValue(stringKey)); //获取的缓存值不为空

            Assert.IsNotNull(CacheProvider.Get(stringKey)); //获取的缓存对象不为空

            Thread.Sleep(TimeSpan.FromSeconds(2.1d));  //暂停2.1秒，因为上边滑动过期时间为2秒，用于检查下边这个缓存获取值

            Assert.IsNull(CacheProvider.Get(stringKey));//因为sleep了两秒所以这个地方断言缓存是空的
        }

        /// <summary>
        /// 使用扩展方法添加复杂的项目（主要是T key）
        /// </summary>
        [TestMethod]
        public void CanAddItemToCacheWithComplexKeyWithExtension()
        {
            var myObject1 = CacheProvider.GetOrCreate("extension",
                () => new CacheValueOf<TestCacheObject>(new TestCacheObject("hello-1")));
            var myObject2 = CacheProvider.GetOrCreate("extension2",
                () => new CacheValueOf<object>(new TestCacheObject("hello-1"), StaticCachePolicy.CreateDefault()));

            var getvalueitem = CacheProvider.GetValue("extension");
            var getvalue = CacheProvider.Get<string,TestCacheObject>("extension");

            CacheProvider.AddOrChange("extension3", new CacheValueOf<string>("extension3"));
            CacheProvider.AddOrChangeValue("extension4", "extension4");


            Assert.IsNotNull(myObject1);

            Assert.IsNotNull(getvalueitem);

            Assert.IsNotNull(getvalue);

            Assert.AreEqual(myObject1.AlreadyExisted, false); //非以存在的

            Assert.AreEqual(myObject1.WasInserted, true); //表示该缓存是插入状态

            Assert.IsNotNull(myObject1.Value); //缓存值不为空

            Assert.AreEqual(myObject2.AlreadyExisted, false); //非以存在的

            Assert.AreEqual(myObject2.WasInserted, true); //表示该缓存是插入状态

            Assert.IsNotNull(myObject2.Value); //缓存值不为空


            Assert.IsNotNull(CacheProvider.GetValue("extension2")); //获取的缓存值不为空

            Assert.IsNotNull(CacheProvider.Get("extension2")); //获取的缓存对象不为空


            Assert.AreEqual(CacheProvider.Get<string,string>("extension3").Item, "extension3");

            Assert.IsNotNull(CacheProvider.GetValue("extension4"), "extension4");
            
        }

        /// <summary>
        /// 添加复杂项目
        /// </summary>
        [TestMethod]
        public void CanAddItemToCacheWithComplexKey()
        {
            var myobject1Key = Web7CacheKey.Create("my-1");
            var myObject1 = new TestCacheObject("test-1");

            var myObject2 = new TestCacheObject("test-2");
            var myObject2Key = Web7CacheKey.Create("my-2");

            var myObject3 = new TestCacheObject("test-3");
            var myObject3Key =
                Web7CacheKey.Create<StrongClassKey>(x => { x.MethodName = "CanAddItemToCacheWithComplexKey"; });

            //var MyObject4 = new TestCacheObject("test-4");


            CacheProvider.AddOrChangeValue(myobject1Key, myObject1);
            CacheProvider.AddOrChangeValue(myObject2Key, myObject2);

            CacheProvider.AddOrChangeValue(myObject3Key, myObject3); //扩展key

            //var policyitem = new CacheItemPolicy  //初始化规则（仅适用于runtimeCache）
            //{
            //    AbsoluteExpiration = DateTimeOffset.Now.AddMinutes(10.0),//滑动过期时间
            //    Priority = CacheItemPriority.NotRemovable,
            //    RemovedCallback = arguments => { }, //移除该缓存项后调用此回调
            //    UpdateCallback = arguments => { } //移除之前调用此回调
            //};
            //policyitem.RemovedCallback += arguments => { };
            //policyitem.ChangeMonitors.Add(new HostFileChangeMonitor(new List<string> { @"C:\1.txt" }));
           
            //CacheProvider.AddOrChange<TestCacheObject>(Web7CacheKey.Create("my-4"), new CacheValueOf<TestCacheObject>(MyObject4, new StaticCachePolicy(policyitem)));

            var object1 = CacheProvider.GetValue(myobject1Key);
            var object2 = CacheProvider.GetValue<TestCacheObject>(myObject2Key);
            var object3 = CacheProvider.GetValue(myObject3Key);

            Assert.IsNull(CacheProvider.GetValue(Web7CacheKey.Create("ah")));
            Assert.IsNull(CacheProvider.GetValue<TestCacheObject>(Web7CacheKey.Create("ah")));
            Assert.AreEqual(object1, myObject1);
            Assert.AreEqual(object2, myObject2);
            Assert.AreEqual(object2.Text, myObject2.Text);
            Assert.AreEqual(object3, object3);
        }

        /// <summary>
        /// 当获取不存在的项时添加该项
        /// </summary>
        [TestMethod]
        public void WhenAddingNonExistantItemResultShowsItemWasCreated()
        {
            bool check = false;

            var result = CacheProvider.GetOrCreate(
                Web7CacheKey.Create("hello"),
                () =>
                {
                    check = true;
                    return new CacheValueOf<TestCacheObject>(new TestCacheObject("whatever"));
                });

            //断言
            Assert.AreEqual(check, true); //该回调执行过了
            Assert.AreEqual(result.AlreadyExisted, false); //不存在
            Assert.AreEqual(result.WasInserted, true);
            Assert.AreEqual(result.WasUpdated, false);
            Assert.AreEqual(result.ExistsButWrongType, false);
            Assert.AreEqual(result.Value.Item.Text, "whatever");
        }


        /// <summary>
        /// 扩展Key
        /// </summary>
        public class StrongClassKey : AbstractEquatableObject<StrongClassKey>
        {
            #region Public Properties

            public string MethodName { get; set; }
            public string[] ParamesHash { get; set; }
            public int MethodHash { get; set; }

            #endregion

            #region Overrides of AbstractEquatableObject<StrongClassKey>

            protected override IEnumerable<PropertyInfo> GetMembersForEqualityComparison()
            {
                yield return this.GetPropertyInfo(x => x.MethodName + MethodHash + string.Join(",", ParamesHash));
            }

            #endregion
        }

        private class TestCacheObject
        {
            #region Constructors and Destructors

            public TestCacheObject(string text)
            {
                Date = DateTime.Now;
                Text = text;
            }

            #endregion

            #region Public Properties

            public DateTime Date { get; set; }
            public string Text { get; set; }

            #endregion
        }
    }
}
